package PartIIWeek1;

import edu.princeton.cs.algs4.*;

public class WordNet3 {
	// maps synset id to synsets string
	private ST<Integer, String> idMap;

	// maps nouns to set of synset ids
	private ST<String, SET<Integer>> nounMap;

	private Digraph G;

	private SAP3 sap3;

	// constructor takes the name of the two input files
	public WordNet3(String synsets, String hypernyms) {
		nounMap = new ST<String, SET<Integer>>();
		idMap = new ST<Integer, String>();

		// The synsets ids are in increasing order.
		// Save the last synset id to determine digraph size.
		int id = -1;

		// http://algs4.cs.princeton.edu/35applications/LookupCSV.java.html
		In in = new In(synsets);
		while (in.hasNextLine()) {
			String line = in.readLine();
			String[] tokens = line.split(",");

			id = Integer.parseInt(tokens[0]);
			String[] nouns = tokens[1].split(" ");

			SET<String> set = new SET<String>();
			for (String noun : nouns) {
				set.add(noun);
			}
			idMap.put(id, tokens[1]);

			for (String noun : nouns) {
				if (nounMap.contains(noun)) {
					nounMap.get(noun).add(id);
				} else {
					SET<Integer> s = new SET<Integer>();
					s.add(id);
					nounMap.put(noun, s);
				}
			}
		}

		assert id != 1;
		this.G = new Digraph(id + 1);

		in = new In(hypernyms);
		while (in.hasNextLine()) {
			String line = in.readLine();
			String[] tokens = line.split(",");

			int synsetId = Integer.parseInt(tokens[0]);

			for (int i = 1; i < tokens.length; i++) {
				// StdOut.println("addEdge " + synsetId + "->" + tokens[i]);
				G.addEdge(synsetId, Integer.parseInt(tokens[i]));
			}
		}

		sap3 = new SAP3(G);

		// check cycle
		DirectedCycle finder = new DirectedCycle(G);
		if (finder.hasCycle()) {
			throw new IllegalArgumentException();
		}

		// Find all roots
		SET<Integer> roots = new SET<Integer>();
		for (int i = 0; i < G.V(); i++) {
			DirectedDFS dfs = new DirectedDFS(G, i);
			if (dfs.count() == 1) {
				roots.add(i);
			}
		}
		assert roots.size() > 0;

		// StdOut.println("roots.size(): " + roots.size());

		if (roots.size() > 1) {
			throw new IllegalArgumentException();
		}
	}

	// returns all WordNet3 nouns
	public Iterable<String> nouns() {
		return nounMap.keys();
	}

	// is the word a WordNet3 noun?
	public boolean isNoun(String word) {
		if (word == null) {
			throw new NullPointerException();
		}

		return nounMap.contains(word);
	}

	// distance between nounA and nounB (defined below)
	public int distance(String nounA, String nounB) {
		if (!isNoun(nounA) || !isNoun(nounB)) {
			throw new IllegalArgumentException();
		}

		SET<Integer> idsA = nounMap.get(nounA);
		SET<Integer> idsB = nounMap.get(nounB);

		// StdOut.println(nounA + ": " + idsA.toString());
		// StdOut.println(nounB + ": " + idsB.toString());

		return sap3.length(idsA, idsB);
	}

	// a synset (second field of synsets.txt) that is the common ancestor of
	// nounA and nounB in a shortest ancestral path (defined below)
	public String sap(String nounA, String nounB) {
		if (!isNoun(nounA) || !isNoun(nounB)) {
			throw new IllegalArgumentException();
		}

		SET<Integer> idsA = nounMap.get(nounA);
		SET<Integer> idsB = nounMap.get(nounB);

		int ancestor = sap3.ancestor(idsA, idsB);
		return idMap.get(ancestor);
	}

	// do unit testing of this class
	public static void main(String[] args) {
		StdOut.println(args[0]);
		StdOut.println(args[1]);
		WordNet3 wordnet = new WordNet3(args[0], args[1]);

		StdOut.println(wordnet.distance("liberalism", "spider_nevus"));
		StdOut.println(wordnet.sap("liberalism", "spider_nevus"));

		StdOut.println(wordnet.distance("cool_medium", "palm_nut"));
		StdOut.println(wordnet.sap("cool_medium", "palm_nut"));

		boolean pass;

		pass = false;
		try {
			wordnet = new WordNet3("synsets3.txt", "hypernymsInvalidTwoRoots.txt");
		} catch (IllegalArgumentException e) {
			pass = true;
		} finally {
			assert pass;
		}

		pass = false;
		try {
			wordnet = new WordNet3("synsets3.txt", "hypernymsInvalidCycle.txt");
		} catch (IllegalArgumentException e) {
			pass = true;
		} finally {
			assert pass;
		}
	}
}
